package evolutionaryballoons.behavior.genetic;

import java.util.Random;

import evolutionaryballoons.Configuration;

import static evolutionaryballoons.Configuration.*;

public class BreedingGrounds {
	private Random random;
	private Programmer programmer;

	public BreedingGrounds() {
		random = new Random();
		programmer = new Programmer();
	}

	private Node crossBreed(Node node1, Node node2) {
		Node child;

		if (random.nextBoolean()) {
			child = node1;
		} else {
			child = node2;
		}

		if (node1 instanceof MultipleNode && node2 instanceof MultipleNode) {
			MultipleNode mChild = (MultipleNode) child;

			if (random.nextFloat() < Configuration.sMutationRandomNode) {
				mChild.leftNode = programmer.buildRandomTree(Configuration.sMultipleNodesSoftLimitMutation);
			} else {
				mChild.leftNode = crossBreed(((MultipleNode) node1).leftNode,
						((MultipleNode) node2).leftNode);
			}

			if (random.nextFloat() < Configuration.sMutationRandomNode) {
				mChild.rightNode = programmer.buildRandomTree(Configuration.sMultipleNodesSoftLimitMutation);
			} else {
				mChild.rightNode = crossBreed(((MultipleNode) node1).rightNode,
						((MultipleNode) node2).rightNode);
			}

			if (random.nextFloat() < sMutationExchangeChildren) {
				Node temp = mChild.leftNode;
				mChild.leftNode = mChild.rightNode;
				mChild.rightNode = temp;
			}
		}

		if (child instanceof MultipleNode) {
			if (random.nextFloat() < sMutationCutNodeFactor) {
				MultipleNode mChild = (MultipleNode) child;

				Node replacement;
				if (random.nextBoolean()) {
					replacement = mChild.leftNode;
				} else {
					replacement = mChild.rightNode;
				}

				child = replacement;
			}
		}

		if (child instanceof DecisionNode) {
			if (random.nextFloat() < sMutationAdjustLimitFactor) {
				DecisionNode dChild = (DecisionNode) child;

				dChild.limit = random.nextFloat() * 2.0f - 1.0f;
			}
		}

		return child;
	}

	public Node createOffspring(Node program1, Node program2) {
		Node source1 = program1.clone();
		Node source2 = program2.clone();

		return crossBreed(source1, source2);
	}
}
